/**
 * 查询字符串序列化配置选项
 */
export interface StringifyOptions {
    /** 是否对值进行URL编码，默认true */
    encode?: boolean
    /** 数组序列化格式，默认'brackets' */
    arrayFormat?: 'brackets' | 'indices' | 'comma' | 'repeat'
    /** 是否跳过null值，默认false */
    skipNulls?: boolean
    /** 是否跳过空字符串，默认false */
    skipEmptyString?: boolean
    /** 自定义编码函数 */
    encoder?: (value: string) => string
    /** 分隔符，默认'&' */
    delimiter?: string
    /** 是否严格模式，默认false */
    strictNullHandling?: boolean
}

/**
 * 支持的值类型
 */
type StringifyValue = string | number | boolean | null | undefined | Date | StringifyValue[] | { [key: string]: StringifyValue }

/**
 * 默认配置
 */
const DEFAULT_OPTIONS: Required<StringifyOptions> = {
    encode: true,
    arrayFormat: 'brackets',
    skipNulls: false,
    skipEmptyString: false,
    encoder: encodeURIComponent,
    delimiter: '&',
    strictNullHandling: false
}

/**
 * 轻量级查询字符串序列化工具
 * 仿照 Qs.stringify 实现，支持对象、数组、嵌套对象的序列化
 */
export class QueryStringifier {
    private options: Required<StringifyOptions>

    constructor(options: StringifyOptions = {}) {
        this.options = { ...DEFAULT_OPTIONS, ...options }
    }

    /**
     * 将对象序列化为查询字符串
     * @param obj 要序列化的对象
     * @param options 序列化选项（可选）
     * @returns 查询字符串
     */
    stringify(obj: Record<string, StringifyValue>, options?: StringifyOptions): string {
        const opts = options ? { ...this.options, ...options } : this.options
        const pairs: string[] = []

        this.stringifyObject(obj, '', pairs, opts)

        return pairs.join(opts.delimiter)
    }

    /**
     * 递归处理对象序列化
     */
    private stringifyObject(obj: Record<string, StringifyValue>, prefix: string, pairs: string[], options: Required<StringifyOptions>): void {
        Object.keys(obj).forEach((key) => {
            const value = obj[key]
            const fullKey = prefix ? `${prefix}[${key}]` : key

            this.stringifyValue(fullKey, value, pairs, options)
        })
    }

    /**
     * 处理单个值的序列化
     */
    private stringifyValue(key: string, value: StringifyValue, pairs: string[], options: Required<StringifyOptions>): void {
        // 处理 null 和 undefined
        if (value === null) {
            if (options.strictNullHandling) {
                pairs.push(this.encodeKey(key, options))
                return
            }
            if (!options.skipNulls) {
                pairs.push(`${this.encodeKey(key, options)}=`)
            }
            return
        }

        if (value === undefined) {
            return
        }

        // 处理空字符串
        if (value === '' && options.skipEmptyString) {
            return
        }

        // 处理数组
        if (Array.isArray(value)) {
            this.stringifyArray(key, value, pairs, options)
            return
        }

        // 处理对象
        if (typeof value === 'object' && value !== null && !(value instanceof Date)) {
            this.stringifyObject(value as Record<string, StringifyValue>, key, pairs, options)
            return
        }

        // 处理基本类型
        const stringValue = this.valueToString(value)
        pairs.push(`${this.encodeKey(key, options)}=${this.encodeValue(stringValue, options)}`)
    }

    /**
     * 处理数组序列化
     */
    private stringifyArray(key: string, array: StringifyValue[], pairs: string[], options: Required<StringifyOptions>): void {
        if (array.length === 0) {
            return
        }

        switch (options.arrayFormat) {
            case 'brackets':
                // format: key[]=value1&key[]=value2
                array.forEach((item) => {
                    this.stringifyValue(`${key}[]`, item, pairs, options)
                })
                break

            case 'indices':
                // format: key[0]=value1&key[1]=value2
                array.forEach((item, index) => {
                    this.stringifyValue(`${key}[${index}]`, item, pairs, options)
                })
                break

            case 'comma':
                // format: key=value1,value2
                const values = array.map((item) => this.encodeValue(this.valueToString(item), options))
                pairs.push(`${this.encodeKey(key, options)}=${values.join(',')}`)
                break

            case 'repeat':
                // format: key=value1&key=value2
                array.forEach((item) => {
                    this.stringifyValue(key, item, pairs, options)
                })
                break
        }
    }

    /**
     * 将值转换为字符串
     */
    private valueToString(value: StringifyValue): string {
        if (value instanceof Date) {
            return value.toISOString()
        }

        if (typeof value === 'boolean') {
            return value ? 'true' : 'false'
        }

        return String(value)
    }

    /**
     * 编码键名
     */
    private encodeKey(key: string, options: Required<StringifyOptions>): string {
        return options.encode ? options.encoder(key) : key
    }

    /**
     * 编码值
     */
    private encodeValue(value: string, options: Required<StringifyOptions>): string {
        return options.encode ? options.encoder(value) : value
    }
}

/**
 * 创建默认的查询字符串序列化器实例
 */
const defaultStringifier = new QueryStringifier()

/**
 * 便捷的 stringify 函数，类似于 Qs.stringify
 * @param obj 要序列化的对象
 * @param options 序列化选项
 * @returns 查询字符串
 */
export function stringify(obj: Record<string, StringifyValue>, options?: StringifyOptions): string {
    return defaultStringifier.stringify(obj, options)
}

/**
 * 查询字符串解析配置选项
 */
export interface ParseOptions {
    /** 是否对值进行URL解码，默认true */
    decode?: boolean
    /** 分隔符，默认'&' */
    delimiter?: string
    /** 自定义解码函数 */
    decoder?: (value: string) => string
    /** 数组格式，默认'brackets' */
    arrayFormat?: 'brackets' | 'indices' | 'comma' | 'repeat'
    /** 是否解析数字，默认false */
    parseNumbers?: boolean
    /** 是否解析布尔值，默认false */
    parseBooleans?: boolean
}

/**
 * 解析查询字符串为对象
 * @param str 查询字符串
 * @param options 解析选项
 * @returns 解析后的对象
 */
export function parse(str: string, options: ParseOptions = {}): Record<string, any> {
    const opts = {
        decode: true,
        delimiter: '&',
        decoder: decodeURIComponent,
        arrayFormat: 'brackets' as const,
        parseNumbers: false,
        parseBooleans: false,
        ...options
    }

    if (!str || typeof str !== 'string') {
        return {}
    }

    // 移除开头的 ? 或 #
    const cleanStr = str.replace(/^[?#]/, '')

    if (!cleanStr) {
        return {}
    }

    const result: Record<string, any> = {}
    const pairs = cleanStr.split(opts.delimiter)

    pairs.forEach((pair) => {
        const [key, ...valueParts] = pair.split('=')
        const value = valueParts.join('=')

        if (!key) return

        const decodedKey = opts.decode ? opts.decoder(key) : key
        const decodedValue = opts.decode ? opts.decoder(value || '') : value || ''

        // 解析值类型
        let parsedValue: any = decodedValue

        if (opts.parseNumbers && !isNaN(Number(decodedValue)) && decodedValue !== '') {
            parsedValue = Number(decodedValue)
        } else if (opts.parseBooleans) {
            if (decodedValue === 'true') parsedValue = true
            else if (decodedValue === 'false') parsedValue = false
        }

        // 处理数组格式
        if (decodedKey.endsWith('[]')) {
            const arrayKey = decodedKey.slice(0, -2)
            if (!result[arrayKey]) result[arrayKey] = []
            result[arrayKey].push(parsedValue)
        } else if (decodedKey.includes('[') && decodedKey.includes(']')) {
            // 处理嵌套对象格式 key[subkey] 或 key[0]
            setNestedValue(result, decodedKey, parsedValue)
        } else {
            // 处理重复键（repeat格式）
            if (result[decodedKey] !== undefined) {
                if (!Array.isArray(result[decodedKey])) {
                    result[decodedKey] = [result[decodedKey]]
                }
                result[decodedKey].push(parsedValue)
            } else {
                result[decodedKey] = parsedValue
            }
        }
    })

    return result
}

/**
 * 设置嵌套对象的值
 */
function setNestedValue(obj: Record<string, any>, key: string, value: any): void {
    const match = key.match(/^([^[]+)\[([^\]]*)\](.*)$/)
    if (!match) {
        obj[key] = value
        return
    }

    const [, mainKey, subKey, rest] = match

    if (!obj[mainKey]) {
        obj[mainKey] = isNaN(Number(subKey)) ? {} : []
    }

    if (rest) {
        setNestedValue(obj[mainKey], subKey + rest, value)
    } else {
        if (Array.isArray(obj[mainKey])) {
            obj[mainKey][Number(subKey)] = value
        } else {
            obj[mainKey][subKey] = value
        }
    }
}

// 导出默认实例，方便直接使用
export const Qs = {
    stringify,
    parse
}

export default Qs
